package main

import (
	"bytebuf"
	"fmt"
	"frame"
	"io/ioutil"
	"mylog"
	"net"
	"strconv"
	"strings"
	"time"
)

var ftpAddress = "192.168.168.102"
var ftpPort = 2121
var username = "changfeng"
var password = "123456"

var port = 9000

var defaultAdDownloadDir = "/default_ad/2017-05-22 193854"
var defaultAdDownloadDir2 = "\\default_ad\\2017-06-16 110929"
var normalAdDownloadDir = "\\normal_ad\\2017-05-22 193854"
var instantAdDownloadDir = "\\instant_ad\\2017-05-22 193854"
var instantAdDownloadDir2 = "instant_ad\\2017-06-16 112102"
var instantAdDownloadDir3 = "\\instant_ad\\2017-06-16 210541"
var announceAdDownloadDir = "\\announce_ad\\2017-05-22 193854"
var apkDownloadDir = "\\app\\apk"
var appConfigDownloadDir = "app_config"
var logoDownloadDir = "logo"
var splashDownloadDir = "splash"

var textAdDir = "E:\\smpleftp\\text_ad"

var textAd1Path = textAdDir + "\\" + "text_ad_1.json"

var defaultTextAdPathPrefix = textAdDir + "\\" + "default_text_ad"

const MaxBufSize = 2018

var connMap = make(map[net.Conn]*Session)

type Session struct {
	conn            net.Conn
	mac             string
	hardwareVersion string
	systemVersion   string
	softwareVersion string
	deviceNumber    string
}

func getFileContent(path string) string {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	return string(b)
}

func init() {
	mylog.InitLogger()
}

func main() {

	//服务器部分
	fmt.Println("Starting the server")
	// 创建listener
	listener, err := net.Listen("tcp", ":"+strconv.Itoa(port))
	if err != nil {
		fmt.Println("Error listening", err.Error())
		mylog.LogE(err)
		return
	}

	mylog.LogE("监听服务器 端口：" + strconv.Itoa(port))

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Error accepting", err.Error())
			return
		}
		fmt.Println("connected:" + conn.RemoteAddr().String())
		go doServerStuff(conn)
	}

}

func doServerStuff(conn net.Conn) {
	buf := make([]byte, MaxBufSize)
	readWriter := frame.NewFrameReadWriter()
	for {
		count, err := conn.Read(buf)

		if err != nil {
			if connMap[conn] != nil {
				mylog.LogE(fmt.Sprintf("读取连接出错 %s ", connMap[conn].mac), err)
				delete(connMap, conn)
			} else {
				mylog.LogE(fmt.Sprintf("读取连接出错"), err)
			}
			return
		}

		rawBuf := buf[:count]
		readWriter.Read(conn, rawBuf, doFrame)
	}
}

func doFrame(conn net.Conn, buf []byte) {
	code := frame.GetFrameCode(buf)
	switch code {
	case frame.CodeLogin:
		handleLogin(conn, buf)
	case frame.CodeHeartBeat:
		handleHeartbeat(conn, buf)
	case frame.CodeState:
		handleState(conn, buf)
	case frame.CodeDownloadFileRequest:
		handleDownloadFileRequestResult(conn, buf)
	case frame.CodeDownloadState:
		handleDownloadState(conn, buf)
	default:

	}
}

func handleLogin(conn net.Conn, buf []byte) {
	readBuf := bytebuf.NewByteBufWith(frame.LenHead, buf, false)

	mac := readBuf.ReadUtf8WithLen1()
	hardwareVersion := readBuf.ReadUtf8WithLen1()
	systemVersion := ""
	softwareVersion := readBuf.ReadUtf8WithLen1()
	deviceNumber := readBuf.ReadUtf8WithLen1()

	sendLoginResult(conn, buf)

	mylog.LogI(fmt.Sprintf("终端登录 %s %s %s %s %s", mac, hardwareVersion, systemVersion, softwareVersion, deviceNumber))

	connMap[conn] = &Session{
		conn:            conn,
		mac:             mac,
		hardwareVersion: hardwareVersion,
		systemVersion:   systemVersion,
		softwareVersion: softwareVersion,
		deviceNumber:    deviceNumber,
	}

	if needUpdate(mac, hardwareVersion, systemVersion, softwareVersion, deviceNumber) {
		mylog.LogE(fmt.Sprintf("需要更新 %s %s %s", mac, hardwareVersion, softwareVersion))
		sendUpdateApkRequest(conn)
	} else {
		mylog.LogE(fmt.Sprintf("不需要需要更新 %s %s %s", mac, hardwareVersion, softwareVersion))
		sendSyncTime(conn)
	}
}

func handleHeartbeat(conn net.Conn, buf []byte) {
	frameCode := frame.GetFrameCode(buf)
	frameNo := frame.GetFrameNo(buf)

	sendUniversalResponse(conn, frameCode, frameNo, frame.UniversalResultSuccess, "")
}

func handleDownloadState(conn net.Conn, buf []byte) {
	readBuf := bytebuf.NewByteBufWith(frame.LenHead, buf, false)
	state := readBuf.ReadByte()
	precision := readBuf.ReadUint8()
	decimal := readBuf.ReadUint8()
	info := readBuf.ReadUtf8WithLen2()

	if state != byte(frame.DownloadStateTransferring) {
		mac := connMap[conn].mac
		mylog.LogI(fmt.Sprintf("下载文件状态 %s  %02d.%02d%% info:%s\r\n", mac, precision, decimal, info))
	}
}

func handleDownloadFileRequestResult(conn net.Conn, buf []byte) {
	readBuf := bytebuf.NewByteBufWith(frame.LenHead, buf, false)
	state := readBuf.ReadByte()
	mac := connMap[conn].mac

	mylog.LogI(fmt.Sprintf("请求下载文件应答 %s state:%02X", mac, state))
}

func handleState(conn net.Conn, buf []byte) {
	readBuf := bytebuf.NewByteBufWith(frame.LenHead, buf, false)

	_ = readBuf.ReadByte()
	mac := readBuf.ReadUtf8WithLen1()

	bodyLen := frame.GetFrameBodyLen(buf)

	end := readBuf.Pos + bodyLen - 2 - len([]byte(mac))

	var stateList [] frame.DeviceState

	for ; readBuf.Pos < end; {
		stateNo := readBuf.ReadUint8()
		state := readBuf.ReadByte()
		info := readBuf.ReadUtf8WithLen2()
		stateList = append(stateList, frame.DeviceState{
			No:    stateNo,
			State: state,
			Info:  info,
		})
	}

	sb := strings.Builder{}

	for _, s := range stateList {
		sb.WriteString(fmt.Sprintf("[%d %d %s]", s.No, s.State, s.Info))
	}

	mylog.LogI(fmt.Sprintf("终端状态 %s %s", mac, sb.String()))
}

func sendLoginResult(conn net.Conn, buf []byte) {
	frameCode := frame.GetFrameCode(buf)
	frameNo := frame.GetFrameNo(buf)

	sendUniversalResponse(conn, frameCode, frameNo, frame.UniversalResultSuccess, "")
}

func sendSyncTime(conn net.Conn) {
	current := time.Now().Unix() * 1000
	syncTime := frame.PackSyncTime(current)

	sendFrame(conn, syncTime)
}

// 下发文件请求
func sendDownLoadFileCmd(conn net.Conn, ftpAddress string, ftpPort int, username string, password string, fileType frame.FileType, ftpDir string) {
	downloadFileRequest := frame.PackDownloadFileRequest(ftpAddress, ftpPort, username, password, fileType, ftpDir)
	sendFrame(conn, downloadFileRequest)
}

func sendUniversalResponse(conn net.Conn, frameCode byte, frameNo byte, result frame.UniversalResult, info string) {
	universalResponse := frame.PackUniversalResult(frameCode, frameNo, result, info)
	sendFrame(conn, universalResponse)
}

func needUpdate(mac string, hardwareVersion string, systemVersion string, softwareVersion string, deviceNumber string) bool {
	return hardwareVersion == "RK30board" && softwareVersion != "2.1.2018-08-31-092915"
}

func sendUpdateApkRequest(conn net.Conn) {
	sendDownLoadFileCmd(conn, ftpAddress, ftpPort, username, password, frame.FileTypeApk, apkDownloadDir)
}

func sendFrame(conn net.Conn, buf []byte) {
	if _, err := conn.Write(buf); err != nil {
		mac := connMap[conn].mac
		mylog.LogE(fmt.Sprintf("发送消息帧失败 %s 帧编号:%02X %s\r\n", mac, frame.GetFrameCode(buf), toHex(buf)))
	}
}
